package com.davidhigenbottam.HeatMap.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.davidhigenbottam.HeatMap.shared.ColourBand;
import com.davidhigenbottam.HeatMap.client.ColourBands;
import com.davidhigenbottam.HeatMap.data.ProvinceData;
import com.davidhigenbottam.HeatMap.shared.Province;

public class ProvinceKML {
	private static final int LEGEND_COLOUR_WIDTH= 84;
	private static final String LEGEND_URL="http://studentheatmap.appspot.com/legend.png";
	private static final String GOOGLE_CHART_URL_PREFIX="http://chart.googleapis.com/chart?chst=d_text_outline&chld=ffffff|12|l|000000|_|";
	private static final Logger log = Logger.getLogger(ProvinceKML.class.getName());

	private static ProvinceKML instance = null;

	private Document doc;

	private ProvinceKML(String kmlUrl) {
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder docBuilder;

		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse(kmlUrl);
			cleanup();
			addLegendScreenOverlays();
		} catch (ParserConfigurationException e) {
			log.severe(e.toString());
			throw new RuntimeException();
		} catch (SAXException e) {
			log.severe(e.toString());
			throw new RuntimeException();
		} catch (IOException e) {
			log.severe(e.toString());
			throw new RuntimeException();
		}
	}

	public void resetColours() {
		Document doc = getDoc();
		NodeList placemarks = doc.getElementsByTagName("Placemark");
		for (int i = 0; i < placemarks.getLength(); i++) {
			Element placemark = (Element) placemarks.item(i);

			String defaultColour = ColourBands
					.fetchProvinceColor(ColourBand.NODATA);

			Element polyStyle = (Element) placemark.getElementsByTagName(
					"PolyStyle").item(0);

			if (polyStyle != null) {
				addOrModifySimpleChildElement(doc, polyStyle, "color",
						defaultColour);

			}
		}
	}

	public void stream(PrintWriter writer) {
		// Output the XML

		try {
			System.setProperty("javax.xml.transform.TransformerFactory",
					"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");

			// Use a Transformer for output
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();

			DOMSource source = new DOMSource(getDoc());
			StreamResult result = new StreamResult(writer);
			transformer.transform(source, result);
		} catch (TransformerConfigurationException e1) {
			log.severe(e1.toString());
			throw new RuntimeException();
		} catch (TransformerFactoryConfigurationError e1) {
			log.severe(e1.toString());
			throw new RuntimeException();
		} catch (TransformerException e1) {
			log.severe(e1.toString());
			throw new RuntimeException();
		}

	}

	public void setColours(ProvinceData pd) {

		Document doc = getDoc();

		NodeList placemarks = doc.getElementsByTagName("Placemark");
		for (int i = 0; i < placemarks.getLength(); i++) {
			Element placemark = (Element) placemarks.item(i);

			String name = placemark.getElementsByTagName("name").item(0)
					.getTextContent();
			ColourBand cb = ColourBand.NODATA;
			String description="";
			Province p = pd.getProvince(name);
			if (p != null) {
				cb = p.getColourBand();
				description= pd.getDescription()+":"+p.getValueDescription();
			} else {
				log.warning("Could not find province:" + name);
			}
			
			addOrModifySimpleChildElement(doc, placemark, "description",
					description);
			
			String provinceColor = ColourBands.fetchProvinceColor(cb);
			//log.info("provinceColor:" + provinceColor);

			// expect one poly style
			Element polyStyle = (Element) placemark.getElementsByTagName(
					"PolyStyle").item(0);
			addOrModifySimpleChildElement(doc, polyStyle, "color",
					provinceColor);

		}
	}

	private void cleanup() {

		Document doc = getDoc();

		NodeList placemarks = doc.getElementsByTagName("Placemark");
		//log.info("number of placemarks:" + placemarks.getLength());
		for (int i = 0; i < placemarks.getLength(); i++) {
			Element placemark = (Element) placemarks.item(i);

			// find the province
			String provinceName = "";
			String defaultColour = ColourBands
					.fetchProvinceColor(ColourBand.NODATA);

			// ASSUMPTION - only one SimpleData element per Placemark
			Element simpleData = (Element) placemark.getElementsByTagName(
					"SimpleData").item(0);
			if (simpleData != null) {
				if (simpleData.getAttribute("name").equals("PROV")) {
					provinceName = simpleData.getTextContent();
				}
			}

			//log.info("provinceName:" + provinceName);

			// add name and description child element
			addOrModifySimpleChildElement(doc, placemark, "name", provinceName);

			addOrModifySimpleChildElement(doc, placemark, "description", ""); // blank
																				// for
																				// now
			// expect one poly style
			Element polyStyle = (Element) placemark.getElementsByTagName(
					"PolyStyle").item(0);

			if (polyStyle == null) {
				log.info("polyStyles is null");
				log.info("i:" + i);
			} else {
				addOrModifySimpleChildElement(doc, polyStyle, "color",
						defaultColour);
				addOrModifySimpleChildElement(doc, polyStyle, "fill", "1");
				addOrModifySimpleChildElement(doc, polyStyle, "outline", "1");
			}

		}

	}

	public void addOrModifySimpleChildElement(Document doc, Element parent,
			String name, String value) {

		// expect one child element if not there add it
		NodeList nl = parent.getElementsByTagName(name);
		Element e;
		if (nl.getLength() == 0) {
			e = doc.createElement(name);
			parent.appendChild(e);
		} else {
			// get existing child element
			e = (Element) nl.item(0);
		}
		// set value
		e.setTextContent(value);
	}

	public Document getDoc() {
		return doc;
	}

	public void setDoc(Document doc) {
		this.doc = doc;
	}

	public static ProvinceKML getInstance(String kmlUrl) {
		if (instance == null) {
			instance = new ProvinceKML(kmlUrl);
		}
		return instance;
	}

	public void addLegendScreenOverlays() {
		
		addScreenOverlay("legendicon",LEGEND_URL,268,17,0);
		
		for(int i=0;i<6;i++){ //6 numbers defining 5 bands
			addScreenOverlay("keytxt"+i,
					GOOGLE_CHART_URL_PREFIX+"00.00",
					(i*LEGEND_COLOUR_WIDTH)+253, 37,2);
		}
		
		//nodata text
		//addScreenOverlay("nodata",GOOGLE_CHART_URL_PREFIX+"No Data", (5*LEGEND_COLOUR_WIDTH)+13, 90,1);
	}
	
		
	public void addScreenOverlay(String id ,String iconHref, int xPosition, int yPosition, int drawOrder) {

		Document doc = getDoc();

		NodeList folderNL = doc.getElementsByTagName("Folder");
		//only one
		Element folder = (Element) folderNL.item(0);
		
		Element so = doc.createElement("ScreenOverlay");
		
		Element icon = doc.createElement("Icon");
		Element href = doc.createElement("href");
		Element color = doc.createElement("color");
		Element drawOrderElem = doc.createElement("drawOrder");
		Element overlayXY = doc.createElement("overlayXY");
		Element screenXY = doc.createElement("screenXY");
		Element visibility = doc.createElement("visibility");
		Element size = doc.createElement("size");
		
		so.setAttribute("id", id);
		href.setTextContent(iconHref);

		//color.setTextContent("ffffffff");
		drawOrderElem.setTextContent(String.valueOf(drawOrder));
		
		overlayXY.setAttribute("x", "0");
		overlayXY.setAttribute("y", "0");
		overlayXY.setAttribute("xunits", "fraction");
		overlayXY.setAttribute("yunits", "fraction");
		screenXY.setAttribute("x", String.valueOf(xPosition));
		screenXY.setAttribute("y", String.valueOf(yPosition));
		screenXY.setAttribute("xunits", "pixels");
		screenXY.setAttribute("yunits", "pixels");
		visibility.setTextContent("1");
		size.setAttribute("x", "-1");
		size.setAttribute("y", "-1");
		size.setAttribute("xunits", "fraction");
		size.setAttribute("yunits", "fraction");
				
		
		icon.appendChild(href);
		so.appendChild(icon);
		so.appendChild(color);
		so.appendChild(drawOrderElem);
		so.appendChild(overlayXY);
		so.appendChild(screenXY);
		so.appendChild(visibility);
		so.appendChild(size);
		
		folder.appendChild(so);
		so.setIdAttribute("id", true);
	}

	public void setLegendBands(ProvinceData provinceData) {
		double[] boundaryValues= provinceData.getColourBandBoundryValues();
		for(int i=0; i<6; i++){
			//"keytxt"+i
			Document doc = getDoc();
			Element screenOverlay = doc.getElementById("keytxt"+i);
			Element href = (Element) screenOverlay.getElementsByTagName("href").item(0);
			href.setTextContent(GOOGLE_CHART_URL_PREFIX+roundDoubleTwoDecimalPlaces(boundaryValues[i]));
		}
	}
	
	public String roundDoubleTwoDecimalPlaces(double d){
		return String.valueOf((double)Math.round(d*100d)/100);
	}
	
}
